﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net.Appender;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using System.Runtime.Serialization.Formatters;
using System.Collections;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Serialization;
using log4net.Core;
using System.Runtime.Remoting.Lifetime;

namespace NHTrace
{
  /// <summary>
  /// Based on RemotingReceiver class from log2console project: http://log2console.codeplex.com/ - Copyright (c) 2007, Rémy Baudet
  /// </summary>
  public class RemotingReceiver : MarshalByRefObject, RemotingAppender.IRemoteLoggingSink, IReceiver
  {
    private object _thisLock = new object();

    #region IRemoteLoggingSink Members

    public void LogEvents(LoggingEvent[] events)
    {
      lock (_thisLock)
      {
        var messages = new List<LogMessage>();
        foreach (LoggingEvent e in events)
        {
          messages.Add(new LogMessage 
          { 
            ApplicationName = e.Domain,
            Message = e.RenderedMessage,
            TimeStamp = e.TimeStamp
          });
        }

        _logger.LogEvents(messages);
      }
    }

    #endregion

    private string _sinkName;// = "LoggingSink";
    private IChannel _channel = null;
    private int _port;// = 7070;
    private Logger _logger;
    private const string RemotingReceiverChannelName = "RemotingReceiverChannel";

    public RemotingReceiver(Logger logger, Dictionary<string, object> receiverParameters)
    {
      _logger = logger;
      _sinkName = (string)receiverParameters["sinkName"];
      _port = (int)receiverParameters["port"]; 
    }

    public override object InitializeLifetimeService()
    {
      return null;
    }

    public void Initialize()
    {
      _channel = ChannelServices.GetChannel(RemotingReceiverChannelName);

      if (_channel == null)
      {
        // Allow clients to receive complete Remoting exception information
        if (RemotingConfiguration.CustomErrorsEnabled(true))
          RemotingConfiguration.CustomErrorsMode = CustomErrorsModes.Off;

        try
        {
          IDictionary props = new Hashtable();
          props["port"] = _port.ToString();
          props["name"] = RemotingReceiverChannelName;
          props["typeFilterLevel"] = TypeFilterLevel.Full;
          _channel = new TcpChannel(props, 
            (BinaryClientFormatterSinkProvider)null, 
            new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full });

          ChannelServices.RegisterChannel(_channel, false);
        }
        catch (Exception ex)
        {
          ExceptionHandler.HandleException(new Exception("Remoting TCP Channel Initialization failed", ex)); 
        }
      }

      Type serverType = RemotingServices.GetServerTypeForUri(_sinkName);
      if ((serverType == null) || (serverType != typeof(RemotingAppender.IRemoteLoggingSink)))
      {
        try
        {
          RemotingServices.Marshal(this, _sinkName, typeof(RemotingAppender.IRemoteLoggingSink));
        }
        catch (Exception ex)
        {
          ExceptionHandler.HandleException(new Exception("Remoting Marshal failed", ex));
        }
      }
    }

    public void Terminate()
    {
      if (_channel != null)
        ChannelServices.UnregisterChannel(_channel);
      _channel = null;
    }
  }
}
